package com.nifostasky.delayed.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * 延迟队列配置
 *      - 需要安装插件
 */
@Configuration
public class RabbitDelayedConfig {
    //交换机 - 队列
    public static final String QUEUE_NAME_DELAYED = "DELAY.QUEUE";
    public static final String EXCHANGE_NAME_DELAYED = "DELAY.EXCHANGE";
    //通配符
    public static final String ROUTING_KEY_DELAYED = "DELAY.#";

    /**
     * 普通队列
     * @return
     */
    @Bean(QUEUE_NAME_DELAYED)
    public Queue queue() {
        return QueueBuilder.durable(QUEUE_NAME_DELAYED).build();
    }

    /**
     * 延时交换机
     * 该类型消息支持延迟投递机制,接收到消息后并未立即将消息投递至目标队列中，
     * 而是存储在mnesia(一个分布式数据系统)表中，
     * 并且当前节点是磁盘节点，那么节点重启后，消息还能保留。
     * 检测消息延迟时间，如达到可投递时间时并将其通过x-delayed-type类型标记的交换机类型投递至目标队列。
     * 但是要注意的是，如果集群中只有一个磁盘节点，如果说磁盘节点丢失，或者节点上的插件失效。
     * 意味着消息将会丢失。
     *
     * 特性
     *
     *     可通过 x-delayed-type 指定类型 为 direct fanout topic 等
     *     检测消息延迟时间，如果达到投递时间，通过x-delayed-type 标记的交换机类型进行投递。
     *     消息存储在声明交换机的那个节点上，消息发送到延时交换机上，消息还未到发送时间，此时停机该节点，消费者不能消费此延时消息，后启动该节点，消息会被重新投递，消费者能够消费此延时消息。
     *     磁盘节点，消息持久化，交换机持久化，消息不会丢失。
     * @return
     */
    @Bean(EXCHANGE_NAME_DELAYED)
    public CustomExchange exchange() {
        Map<String, Object> arguments = new HashMap<>(1);
        // 在这里声明一个主题类型的延迟队列，当然其他类型的也可以。
        arguments.put("x-delayed-type", "topic");
        return new CustomExchange(EXCHANGE_NAME_DELAYED, "x-delayed-message", true, false, arguments);
    }

    @Bean
    public Binding bindingNotify(@Qualifier(QUEUE_NAME_DELAYED) Queue queue, @Qualifier(EXCHANGE_NAME_DELAYED) CustomExchange customExchange) {
        return BindingBuilder.bind(queue).to(customExchange).with(ROUTING_KEY_DELAYED).noargs();
    }
}
